#include <GameEngine/UnitTest/unit_test.h>

#include <GameEngine/Geometry/Surfaces/circle3d.h>


#ifdef GAMEENGINE_NAMESPACES
namespace GameEngine {
#endif


//! Launch the unit tests on Circle3d objects
/*!
 @return True if all unit tests on Circle3d objects were
         succesful, false otherwise
 */
template <class T>
bool UnitTest::test_geometry_circle3d_functions() {
	bool res = true;

	// Get number of bytes
	MemoryManager::BytesCount bytes = MemoryManager::instance()->bytes();

	// Create line
	GameEngine::Point3d<T> center((T)0,(T)0,(T)0);
	T radius = (T)5;
	GameEngine::Vector3d<T> normal((T)1,(T)0,(T)0);
	GameEngine::Circle3d<T> circle(center,radius,normal);

	// Test accessors
	T tolerance = (T)0.00001;
	{{
		res &= UNIT_TEST_ERROR( "[Circle3d] Test center()", ( center.get_square_distance(circle.center()) < tolerance ) );
		res &= UNIT_TEST_ERROR( "[Circle3d] Test radius()", ( abs(circle.radius()-radius) < tolerance ) );
		res &= UNIT_TEST_ERROR( "[Circle3d] Test normal()", ( normal.get_square_distance(circle.normal()) < tolerance ) );
	}}

	// Test diameter and area functions
	{{
		res &= UNIT_TEST_ERROR( "[Circle3d] Test diameter()", ( abs(circle.diameter()-(T)2*radius) < tolerance ) );
		res &= UNIT_TEST_ERROR( "[Circle3d] Test area()", ( abs(circle.area()-(T)PI*radius*radius) < tolerance ) );
	}}

	// Test coplanar function
	{{
		GameEngine::Point3d<T> p1((T)0.0,(T)1.5,(T)0.5);
		GameEngine::Point3d<T> p2((T)0.0,(T)5.5,(T)0.5);
		GameEngine::Point3d<T> p3((T)1.0,(T)radius,(T)0.0);
		res &= UNIT_TEST_ERROR( "[Circle3d] Test coplanar()", ( circle.coplanar(center) == true ) );
		res &= UNIT_TEST_ERROR( "[Circle3d] Test coplanar()", ( circle.coplanar(p1) == true ) );
		res &= UNIT_TEST_ERROR( "[Circle3d] Test coplanar()", ( circle.coplanar(p2) == true ) );
		res &= UNIT_TEST_ERROR( "[Circle3d] Test coplanar()", ( circle.coplanar(p3) == false ) );
	}}

	// Test inside function
	{{
		GameEngine::Point3d<T> p1((T)0.0,(T)1.5,(T)0.5);
		GameEngine::Point3d<T> p2((T)0.0,(T)5.5,(T)0.5);
		GameEngine::Point3d<T> p3((T)0.0,(T)radius,(T)0.0);
		GameEngine::Point3d<T> p4((T)1.0,(T)1.5,(T)0.5);
		res &= UNIT_TEST_ERROR( "[Circle3d] Test inside()", ( circle.inside(p1) == true ) );
		res &= UNIT_TEST_ERROR( "[Circle3d] Test inside()", ( circle.inside(p2) == false ) );
		res &= UNIT_TEST_ERROR( "[Circle3d] Test inside()", ( circle.inside(p3, true) == false ) );
		res &= UNIT_TEST_ERROR( "[Circle3d] Test inside()", ( circle.inside(p3, false) == true ) );
		res &= UNIT_TEST_ERROR( "[Circle3d] Test inside()", ( circle.inside(p4) == false ) );
	}}

	// Test intersection function
	{{
		GameEngine::Point3d<T> p1((T)0,(T)0,(T)0);
		GameEngine::Point3d<T> p2((T)0,(T)1,(T)1);
		GameEngine::Line3d<T> line(p1,p2);
		T coord1, coord2;
		GameEngine::IntersectionType::Type type;
		res &= UNIT_TEST_ERROR( "[Circle3d] Test intersection()", ( circle.intersection(line, coord1, coord2, &type) == 2 ) );
		res &= UNIT_TEST_ERROR( "[Circle3d] Test intersection()", ( abs(coord1+radius/(T)sqrt(2.0)) < tolerance ) );
		res &= UNIT_TEST_ERROR( "[Circle3d] Test intersection()", ( abs(coord2-radius/(T)sqrt(2.0)) < tolerance ) );
		res &= UNIT_TEST_ERROR( "[Circle3d] Test intersection()", ( type == GameEngine::IntersectionType::Intersection ) );
	}}

	// Test intersection function
	{{
		GameEngine::Point3d<T> p1((T)0,(T)0,radius);
		GameEngine::Point3d<T> p2((T)0,(T)1,radius);
		GameEngine::Line3d<T> line(p1,p2);
		T coord1, coord2;
		GameEngine::IntersectionType::Type type;
		res &= UNIT_TEST_ERROR( "[Circle3d] Test intersection()", ( circle.intersection(line, coord1, coord2, &type) == 1 ) );
		res &= UNIT_TEST_ERROR( "[Circle3d] Test intersection()", ( coord1 < tolerance ) );
		res &= UNIT_TEST_ERROR( "[Circle3d] Test intersection()", ( coord2 < tolerance ) );
		res &= UNIT_TEST_ERROR( "[Circle3d] Test intersection()", ( type == GameEngine::IntersectionType::TangentIntersection ) );
	}}

	// Test intersection function
	{{
		GameEngine::Point3d<T> p1((T)0,(T)0,radius);
		GameEngine::Point3d<T> p2((T)1,(T)1,radius);
		GameEngine::Line3d<T> line(p1,p2);
		T coord1, coord2;
		GameEngine::IntersectionType::Type type;
		res &= UNIT_TEST_ERROR( "[Circle3d] Test intersection()", ( circle.intersection(line, coord1, coord2, &type) == 1 ) );
		res &= UNIT_TEST_ERROR( "[Circle3d] Test intersection()", ( coord1 < tolerance ) );
		res &= UNIT_TEST_ERROR( "[Circle3d] Test intersection()", ( coord2 < tolerance ) );
		res &= UNIT_TEST_ERROR( "[Circle3d] Test intersection()", ( type == GameEngine::IntersectionType::TangentIntersection ) );
	}}

	// Test intersection function
	{{
		GameEngine::Point3d<T> p1((T)0,(T)0,radius+(T)1);
		GameEngine::Point3d<T> p2((T)0,(T)1,radius+(T)1);
		GameEngine::Line3d<T> line(p1,p2);
		T coord1, coord2;
		GameEngine::IntersectionType::Type type;
		res &= UNIT_TEST_ERROR( "[Circle3d] Test intersection()", ( circle.intersection(line, coord1, coord2, &type) == 0 ) );
		res &= UNIT_TEST_ERROR( "[Circle3d] Test intersection()", ( type == GameEngine::IntersectionType::NoIntersection ) );
	}}

	// Test closest point function
	{{
		GameEngine::Point3d<T> pt((T)0,(T)10,(T)10);
		GameEngine::Point3d<T> cpt = circle.closest_point(pt);
		res &= UNIT_TEST_ERROR( "[Circle3d] Test closest_point()", ( circle.closest_point(pt).get_square_distance(
			GameEngine::Point3d<T>((T)0,(T)sqrt(2.0)*radius/(T)2,(T)sqrt(2.0)*radius/(T)2)) < tolerance ) );
	}}

	// Get number of bytes
	res &= UNIT_TEST_ERROR( "[Circle3d] Test memory leak", ( bytes == MemoryManager::instance()->bytes() ));

	// Return result
	return res;
}


#ifdef GAMEENGINE_NAMESPACES
} // namespace GameEngine
#endif
